package com.jvyou.singleton.lazy;

/**
 * @author 橘柚
 * @version 1.0-SNAPSHOT
 * @Date 2024/7/29 16:20
 * @Description 双重检查锁模式。上面我们分析出只有在执行`instance = new LockSingleton();`才可能会产生线程安全问题，
 * 而大部分的获取实例都是读操作，读操作是线程安全的，所以没必要对读操作加锁，应该是在`instance==null`的时候才加锁，这样就不会产生性能问题。
 * <p>
 * 双重检查锁模式是一种非常好的单例实现模式，解决了单例、性能、线程安全问题。
 * 上面的双重检测锁模式看上去完美无缺，其实是存在问题，在多线程的情况下，可能会出现空指针问题，出现问题的原因是JVM在实例化对象的时候会进行优化和指令重排序操作。
 * 要解决双重检查锁模式带来空指针异常的问题，只需要使用 `volatile` 关键字, `volatile` 关键字可以保证可见性和有序性。
 */
public class DoubleCheckLockSingleton {

    /*volatile关键字用于确保多线程环境下的可见性，即当一个线程修改了instance的值时，其他线程可以立即看到修改后的值。*/
    private static volatile DoubleCheckLockSingleton instance;

    private DoubleCheckLockSingleton() {

    }

    public static DoubleCheckLockSingleton getInstance() {
        // 如果instance为空，则进行抢锁
        // 第一次检查在同步块之外，减少了进入同步块的机会；第二次检查在同步块之内，确保实例初始化的唯一性。
        if (instance == null) {
            synchronized (DoubleCheckLockSingleton.class) {
                // 抢到锁后，再次判断instance是否为空，如果为空则创建实例
                if (instance == null) {
                    instance = new DoubleCheckLockSingleton();
                }
            }
        }
        return instance;
    }

    @Override
    public String toString() {
        return super.toString();
    }
}
